import UIKit
import JJCollectionViewRoundFlowLayout
import LXExtensions

typealias XXCollectionViewRoundFlowLayout = JJCollectionViewRoundFlowLayout
protocol XCollectionManagerDelegate: NSObjectProtocol {
    func collectDidScroll(manager:XCollectionManager, scrollView:UIScrollView)
    func collectDidEndScroll(manager:XCollectionManager, scrollView:UIScrollView)
    func collectDidLoadCell(manager:XCollectionManager,
                            row:XCollectionRow,
                            cell:UICollectionViewCell,
                            at indexPath:IndexPath)
    func collectDidLoadHeaderFooter(manager:XCollectionManager,
                                    section:XCollectionSection,
                                    headerFooter:UICollectionReusableView,
                                    at indexPath:IndexPath)
    func collectDidClickedCollectionCell(collectManager:XCollectionManager,
                            row:XCollectionRow,
                            at indexPath:IndexPath)
    func collectDidHighlightItem(manager:XCollectionManager, row:XCollectionRow,
                                 at indexPath:IndexPath)
    func collectDidUnhighlightItem(manager:XCollectionManager, row:XCollectionRow,
                                 at indexPath:IndexPath)

    func collectDidReloadData(manager:XCollectionManager)
}
extension XCollectionManagerDelegate {
    func collectDidScroll(manager:XCollectionManager, scrollView:UIScrollView){
    }
    func collectDidEndScroll(manager:XCollectionManager, scrollView:UIScrollView){
    }
    func collectDidLoadCell(manager:XCollectionManager,
                            row:XCollectionRow,
                            cell:UICollectionViewCell,
                            at indexPath:IndexPath){
    }
    func collectDidClickedCollectionCell(collectManager:XCollectionManager,
                            row:XCollectionRow,
                            at indexPath:IndexPath){
    }
    func collectDidLoadHeaderFooter(manager:XCollectionManager,
                                    section:XCollectionSection,
                                    headerFooter:UICollectionReusableView,
                                    at indexPath:IndexPath) {
    }
    func collectDidHighlightItem(manager:XCollectionManager, row:XCollectionRow,
                                 at indexPath:IndexPath) {
        
    }
    
    func collectDidUnhighlightItem(manager:XCollectionManager, row:XCollectionRow,
                                   at indexPath:IndexPath){
        
    }

    func collectDidReloadData(manager:XCollectionManager){
    }
}
class XCollectionManager: NSObject {
    init(layout: UICollectionViewLayout) {
        collectionView = UICollectionView.init(frame: .zero, collectionViewLayout: layout)
        collectionView.showsVerticalScrollIndicator = false
        collectionView.alwaysBounceVertical = true
        super.init()
        defaultCollectionView()
    }
    override convenience init() {
        self.init(layout: JJCollectionViewRoundFlowLayout())
    }
    
    var sections:[XCollectionSection] = []{
        didSet {
        }
    }
    weak var delegate :XCollectionManagerDelegate?
    var configCell: ((IndexPath,XCollectionRow,UICollectionViewCell)->())?
    var configHeaderFooter: ((IndexPath,XCollectionSection,UICollectionReusableView)->())?
    var didClicked: ((IndexPath,XCollectionRow)->())?
    var collectionView: UICollectionView {
        didSet {
            defaultCollectionView()
        }
    }
    func defaultCollectionView() {
        collectionView.contentInsetAdjustmentBehavior = .never
        collectionView.lx.registerCellClass(UICollectionViewCell.self)
        collectionView.lx.registerHeaderClass(UICollectionReusableView.self)
        collectionView.lx.registerFooterClass(UICollectionReusableView.self)
        collectionView.delegate = self
        collectionView.dataSource = self
    }
    //MARK: - 注册cell
    
    public func registCellClasses(cells:[UICollectionViewCell.Type]) {
        cells.forEach { cls in
            collectionView.lx.registerCellClass(cls)
        }
    }
    public func registCellNibs(types:[UICollectionViewCell.Type]) {
        types.forEach { cls in
            collectionView.lx.registerCellNib(cls)
        }
    }
    public func registHeaderClasses(types:[UICollectionReusableView.Type]) {
        types.forEach { cls in
            collectionView.lx.registerHeaderClass(cls)
        }
    }
    public func registHeaderNibs(types:[UICollectionReusableView.Type]) {
        types.forEach { cls in
            collectionView.lx.registerHeaderNib(cls)
        }
    }
    public func registFooterClasses(types:[UICollectionReusableView.Type]) {
        types.forEach { cls in
            collectionView.lx.registerFooterClass(cls)
        }
    }
    public func registFooterNibs(types:[UICollectionReusableView.Type]) {
        types.forEach { cls in
            collectionView.lx.registerFooterNib(cls)
        }
    }
    
    //MARK: - 操作

    func clearData() {
        self.sections = []
        self.collectionView.reloadData()
    }
    func reloadData() {
        self.collectionView.reloadData()
        self.delegate?.collectDidReloadData(manager: self)
    }
    func reloadSections(_ sections:[XCollectionSection])  {
        var indexSet: IndexSet = []
        for section in sections {
            if let index = self.sections.firstIndex(of: section) {
                indexSet.insert(index)
            }
        }
        if !indexSet.isEmpty {
            collectionView.reloadSections(indexSet)
        }
    }
    func reloadRow(row:XCollectionRow, in section: XCollectionSection) {
        guard let sectionIndex = sections.firstIndex(of: section) else {
            return
        }
        guard let rowIndex = section.rows.firstIndex(of: row) else {
            return
        }
        let indexPath = IndexPath.init(item: rowIndex, section: sectionIndex)
        collectionView.reloadItems(at: [indexPath])
    }
    func animate(updates:(() -> Void)?, completion: ((Bool) -> Void)? = nil) {
        collectionView.performBatchUpdates(updates, completion: completion)
    }
    func append(rows: [XCollectionRow]) {
        guard let section = sections.last else {
            return
        }
        append(rows: rows, in: section)
    }
    func append(rows: [XCollectionRow], in section: XCollectionSection) {
        guard let sectionIndex = sections.firstIndex(of: section) else {
            return
        }
        let forceReload = collectionView.numberOfItems(inSection: sectionIndex) != section.rows.count
        let firstRowIndex = section.rows.count
        section.rows.append(contentsOf: rows)
        var indexPathList: [IndexPath] = []
        for i in 0..<rows.count {
            let indexPath = IndexPath(row: firstRowIndex + i, section: sectionIndex)
            indexPathList.append(indexPath)
        }
        if firstRowIndex == 0 || forceReload {
            collectionView.reloadSections(IndexSet.init(arrayLiteral: sectionIndex))
        }else{
            collectionView.insertItems(at: indexPathList)
        }
    }
    func append(sections: [XCollectionSection]) {
        let forceReload = collectionView.numberOfSections != self.sections.count
        let firstSectionIndex = self.sections.count
        var indexPathList: IndexSet = IndexSet.init()
        for i in 0..<sections.count {
            indexPathList.insert(firstSectionIndex + i)
        }
        self.sections.append(contentsOf: sections)
        if firstSectionIndex == 0 || forceReload {
            collectionView.reloadData()
        }else {
            collectionView.insertSections(indexPathList)
        }
    }
    func insetRow(row:XCollectionRow, at index:Int, in section: XCollectionSection) {
        var rowIndex = index
        if section.rows.count < rowIndex {
            rowIndex = section.rows.count
        }
        if rowIndex < 0 {
            rowIndex = 0
        }
        guard let sectionIndex = sections.firstIndex(of: section) else {
            return
        }
        let indexPath = IndexPath.init(item: rowIndex, section: sectionIndex)
        section.rows.insert(row, at: rowIndex)
        collectionView.insertItems(at: [indexPath])
    }
    func removeRow(row:XCollectionRow, in section: XCollectionSection) {
        guard let sectionIndex = sections.firstIndex(of: section) else {
            return
        }
        guard let rowIndex = section.rows.firstIndex(of: row) else {
            return
        }
        let indexPath = IndexPath.init(item: rowIndex, section: sectionIndex)
        section.rows.remove(at: rowIndex)
        collectionView.deleteItems(at: [indexPath])
    }
    func insetSection(section:XCollectionSection, at index:Int) {
        var sectionIndex = index
        if sections.count < sectionIndex {
            sectionIndex = sections.count
        }
        if sectionIndex < 0 {
            sectionIndex = 0
        }
        collectionView.insertSections([sectionIndex])
        sections.insert(section, at: sectionIndex)
    }
    func removeSection(section:XCollectionSection) {
        guard let sectionIndex = sections.firstIndex(of: section) else {
            return
        }
        collectionView.deleteSections([sectionIndex])
        sections.remove(at: sectionIndex)
    }
}
extension XCollectionManager: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout,JJCollectionViewDelegateRoundFlowLayout {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        delegate?.collectDidScroll(manager: self, scrollView: scrollView)
    }
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        DispatchQueue.main.async {
            self.delegate?.collectDidEndScroll(manager: self, scrollView: scrollView)
        }
    }
    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        if !decelerate {
            DispatchQueue.main.async {
                self.delegate?.collectDidEndScroll(manager: self, scrollView: scrollView)
            }
        }
    }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return sections.count
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        let sec = sections[section]
        return sec.rows.count
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let rowModel = sections[indexPath.section].rows[indexPath.row]
        let cell = collectionView.lx.dequeueReusableCell(rowModel.clsName, indexPath: indexPath)!
        rowModel.indexPath = indexPath
        rowModel.collectionManager = self
        configCell?(indexPath,rowModel,cell)
        delegate?.collectDidLoadCell(manager: self, row: rowModel, cell: cell, at: indexPath)
        if let prot = cell as? XCollectionRowProtocol {
            prot.x_dataSourceDidSet(rowModel)
        }else if cell.responds(to: NSSelectorFromString("x_dataSourceDidSet:")) {
            cell.perform(NSSelectorFromString("x_dataSourceDidSet:"), with: rowModel)
        }
        return cell
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        let sec = sections[section]
        return sec.minimumLineSpacing
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        let sec = sections[section]
        return sec.minimumInteritemSpacing
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        let sec = sections[section]
        return sec.sectionInsets
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let rowModel = sections[indexPath.section].rows[indexPath.row]
        if rowModel.size.height < 0 || rowModel.size.width < 0 {
            return .zero
        }
        return rowModel.size
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        let sec = sections[section]
        return sec.header.size
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
        let sec = sections[section]
        return sec.footer.size
    }
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let sec = sections[indexPath.section]
        if kind == UICollectionView.elementKindSectionHeader {
            let header = collectionView.lx.dequeueReusableHeader(sec.header.clsName, indexPath: indexPath)!
            configHeaderFooter?(indexPath,sec,header)
            delegate?.collectDidLoadHeaderFooter(manager: self, section: sec, headerFooter: header, at: indexPath)
            if let prot = header as? XCollectionSectionHeaderFooterProtocol {
                prot.x_dataSourceDidSet(sec.header)
            }else if header.responds(to: NSSelectorFromString("x_dataSourceDidSet:")) {
                header.perform(NSSelectorFromString("x_dataSourceDidSet:"), with: sec.header)
            }
            return header
        }else{
            let footer = collectionView.lx.dequeueReusableFooter(sec.footer.clsName, indexPath: indexPath)!
            configHeaderFooter?(indexPath,sec,footer)
            delegate?.collectDidLoadHeaderFooter(manager: self, section: sec, headerFooter: footer, at: indexPath)
            if let prot = footer as? XCollectionSectionHeaderFooterProtocol {
                prot.x_dataSourceDidSet(sec.footer)
            }else if footer.responds(to: NSSelectorFromString("x_dataSourceDidSet:")) {
                footer.perform(NSSelectorFromString("x_dataSourceDidSet:"), with: sec.footer)
            }
            return footer
        }
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, configModelForSectionAt section: Int) -> JJCollectionViewRoundConfigModel {
        let sec = sections[section]
        let model = JJCollectionViewRoundConfigModel()
        if sec.backgroundColor != nil {
            model.backgroundColor = sec.backgroundColor!
        }
        model.cornerRadius = sec.backgroundCornerRadius
        return model
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, borderEdgeInsertsForSectionAt section: Int) -> UIEdgeInsets {
        let sec = sections[section]
        return sec.backgroundSectionInsets
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, isCalculateHeaderViewIndex section: Int) -> Bool {
        let sec = sections[section]
        return sec.isCalculateHeader
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, isCalculateFooterViewIndex section: Int) -> Bool {
        let sec = sections[section]
        return sec.isCalculateFooter
    }
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        DispatchQueue.main.async {
            let rowModel = self.sections[indexPath.section].rows[indexPath.row]
            self.didClicked?(indexPath,rowModel)
            self.delegate?.collectDidClickedCollectionCell(collectManager: self, row: rowModel, at: indexPath)
            rowModel.delegate?.rowDidSelect(row: rowModel, at: indexPath)
        }
    }
    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        let rowModel = sections[indexPath.section].rows[indexPath.row]
        if let prot = cell as? XCollectionRowProtocol {
            prot.x_dataSourceWillDisplay(rowModel)
        }else if cell.responds(to: NSSelectorFromString("x_dataSourceWillDisplay:")) {
            cell.perform(NSSelectorFromString("x_dataSourceWillDisplay:"), with: rowModel)
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
        let rowModel = sections[indexPath.section].rows[indexPath.row]
        delegate?.collectDidHighlightItem(manager: self, row: rowModel, at: indexPath)
    }
    
    func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
        let rowModel = sections[indexPath.section].rows[indexPath.row]
        delegate?.collectDidUnhighlightItem(manager: self, row: rowModel, at: indexPath)
    }
    
}
class XCollectionSection: NSObject {
    var header : XCollectionSectionHeaderFooter = XCollectionSectionHeaderFooter()
    var footer : XCollectionSectionHeaderFooter = XCollectionSectionHeaderFooter()
    var rows : [XCollectionRow] = []
    var sectionInsets: UIEdgeInsets = .zero
    var minimumInteritemSpacing : CGFloat = 0
    var minimumLineSpacing : CGFloat = 0
    var backgroundColor : UIColor?
    var backgroundCornerRadius : CGFloat = 0
    var isCalculateHeader : Bool = false
    var isCalculateFooter : Bool = false
    var backgroundSectionInsets : UIEdgeInsets = .zero
    
    override init() {
        super.init()
    }
    
    init(configure: @escaping (XCollectionSection) -> Void) {
        super.init()
        configure(self)
    }
}

protocol XCollectionRowDelegate: NSObjectProtocol {
    func rowDidSelect(row: XCollectionRow, at indexPath: IndexPath)
    func rowNeedRemove(row: XCollectionRow, at indexPath: IndexPath)
}

extension XCollectionRowDelegate {
    func rowDidSelect(row: XCollectionRow, at indexPath: IndexPath) {
        
    }
    func rowNeedRemove(row: XCollectionRow, at indexPath: IndexPath) {
        _ = row.removeFromCollection()
    }
}

class XCollectionRow: NSObject {
    var size :CGSize = .zero
    var clsName: UICollectionViewCell.Type = UICollectionViewCell.self
    var model :Any?
    var extraData :Any?
    var identify :String?
    
    var indexPath :IndexPath?
    weak var collectionManager: XCollectionManager?
    var customRemoveBlock: (()->())?
    weak var delegate: XCollectionRowDelegate?

    override init() {
        super.init()
        customRemoveBlock = { [weak self] in
            _ = self?.removeFromCollection()
        }
    }
    
    init(configure: @escaping (XCollectionRow) -> Void) {
        super.init()
        configure(self)
        customRemoveBlock = { [weak self] in
            _ = self?.removeFromCollection()
        }
    }

    func removeFromCollection(animated: Bool = false) -> Bool {
        guard let manager = collectionManager else { return false }
        guard let indexPath = indexPath else { return false }
        if manager.sections.count <= indexPath.section {
            return false
        }
        let section = manager.sections[indexPath.section]
        if section.rows.count <= indexPath.row {
            return false
        }
        section.rows.remove(at: indexPath.row)
        if animated {
            manager.animate {
                manager.reloadData()
            }
        }else{
            manager.reloadData()
        }
        return true
    }
}
protocol XCollectionRowProtocol {
    func x_dataSourceDidSet(_ xRow:XCollectionRow)
    func x_dataSourceWillDisplay(_ xRow:XCollectionRow)
}
extension XCollectionRowProtocol {
    func x_dataSourceWillDisplay(_ xRow:XCollectionRow) {
    }
}
protocol XCollectionSectionHeaderFooterProtocol {
    func x_dataSourceDidSet(_ hf:XCollectionSectionHeaderFooter)
}
class XCollectionSectionHeaderFooter: NSObject {
    var clsName: UICollectionReusableView.Type = UICollectionReusableView.self
    var size :CGSize = .zero
    var model :Any?
    var extraData :Any?
}


